#ifndef VL_ANGLE_HPP
#define VL_ANGLE_HPP

#include <climits>
//#include <iostream>
//#include <stdexcept>

namespace vl
{

// Usage: shift_<x, x >= 0>::run(v)
template<int by, bool sign>
struct shift_
{
	
};

template<int by>
struct shift_<by, false>
{
	static int run(int v)
	{
		return v << (-by);
	}
};

template<int by>
struct shift_<by, true>
{
	static int run(int v)
	{
		return v >> by;
	}
};

double const pi = 3.14159265358979323846;

template<int Prec>
class basic_angle
{
public:
	typedef int value_type;
	
	static const int bits_in_type = CHAR_BIT * sizeof(value_type);
	static const unsigned int prec = Prec;
	static const unsigned int count = (1 << prec);
	static const unsigned int mask = count - 1;

	static const int rel_clamp_bit = (1 << (prec - 1));
	static const int rel_pos_mask = (1 << (prec - 1)) - 1;
	static const int rel_neg_bits = ~rel_pos_mask;
	static const int rel_shift_steps = bits_in_type - prec;
	
	static basic_angle from_rad(double rad)
	{
		return basic_angle( value_type(rad * (double(count) / (2 * 3.14159265358979323846))) );
	}

	basic_angle()
	: data(0)
	{
	}
	
	explicit basic_angle(value_type data_)
	: data(data_)
	{
	}
	
	explicit basic_angle(double degrees)
	: data(value_type(degrees * (double(count) / 360.0)))
	{
		
	}
	
	static basic_angle almost(double degrees)
	{
		basic_angle ang(degrees);
		return basic_angle(ang.data - 1);
	}
	
	basic_angle& operator=(value_type b)
	{
		data = b;
		return *this;
	}
	
	basic_angle operator+(basic_angle b) const
	{
		return basic_angle(data) += b;
	}
	
	basic_angle& operator+=(basic_angle b)
	{
		data += b.data;
		return *this;
	}
	
	basic_angle operator-(basic_angle b) const
	{
		return basic_angle(data) -= b;
	}
	
	basic_angle& operator-=(basic_angle b)
	{
		data -= b.data;
		return *this;
	}
	
	basic_angle operator-() const
	{
		return basic_angle(-data);
	}
	
	template<class T2>
	basic_angle operator*(T2 b) const
	{
		return basic_angle(data) *= b;
	}
	
	template<class T2>
	basic_angle& operator*=(T2 b)
	{
		data *= b;
		return *this;
	}
	
	basic_angle operator/(value_type b)
	{
		return basic_angle(data) /= b;
	}
	
	basic_angle& operator/=(value_type b)
	{
		data /= b;
		return *this;
	}
	
	
	
	template<int bits>
	value_type adjust()
	{
		return shift_<prec - bits, prec >= bits>::run(data);
	}
	
	basic_angle muldiv(value_type mul, value_type div)
	{
		long long d = data;
		return basic_angle(value_type((d * mul) / div));
	}
	
	friend basic_angle abs(basic_angle ang)
	{
		return ang.data >= 0 ? ang : basic_angle(-ang.data);
	}
	
	bool operator<(basic_angle b) const { return data < b.data; }
	bool operator>(basic_angle b) const { return data > b.data; }
	bool operator<=(basic_angle b) const { return data <= b.data; }
	bool operator>=(basic_angle b) const { return data >= b.data; }
	bool operator==(basic_angle b) const { return data == b.data; }
	bool operator!=(basic_angle b) const { return data != b.data; }
		
/*
	friend std::ostream& operator<<(std::ostream& str, basic_angle a)
	{
		return str << a.to_deg();
	}
	
	friend std::istream& operator>>(std::istream& str, basic_angle& a)
	{
		double deg;
		str >> deg;
		a = basic_angle(deg);
		return str;
	}
*/
	basic_angle relative(basic_angle b)
	{
		return basic_angle(((b.data - data) << rel_shift_steps) >> rel_shift_steps);
	}
	
	double to_rad()
	{
		return double(data) * ((2 * 3.14159265358979323846) / double(count));
	}
	
	double to_deg()
	{
		return double(data) * (360.0 / double(count));
	}
	
	void clamp()
	{
		data &= mask;
	}

	value_type data;
};

typedef basic_angle<24> angle;

}

#endif //VL_ANGLE_HPP
